#define _POSIX_C_SOURCE 200809L
#include "server.h"
#include "util.h"
#include "output.h"
#include "seat.h"
#include <glib.h>
#include <gio/gio.h>
#include <glib-unix.h>

#include <wlr/util/log.h>
#include <wlr/types/wlr_xdg_output_v1.h>
#include <wlr/types/wlr_server_decoration.h>
#include <wlr/types/wlr_xdg_decoration_v1.h>
#include <wlr/types/wlr_presentation_time.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/backend/libinput.h>

#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <libinput.h>

static gboolean handle_sigterm(gpointer user_data)
{
    Server* server = (Server*)user_data;
    g_main_loop_quit(server->glib_loop);
    return G_SOURCE_REMOVE;
}

Server *server_create(void)
{
    Server* server = ecalloc(1, sizeof(*server));

    /* The Wayland display is managed by libwayland. It handles accepting
     * clients from the Unix socket, manging Wayland globals, and so on. */
    server->dpy = wl_display_create();

    server->glib_loop = g_main_loop_new(NULL, FALSE);
    GSource* source = wl_glib_source_new(server->dpy);
    g_source_attach(source, NULL);
    g_source_unref(source);


    g_unix_signal_add(SIGINT, handle_sigterm, server);
    g_unix_signal_add(SIGTERM, handle_sigterm, server);

    /* The backend is a wlroots feature which abstracts the underlying input and
     * output hardware. The autocreate option will choose the most suitable
     * backend based on the current environment, such as opening an X11 window
     * if an X11 server is running. The NULL argument here optionally allows you
     * to pass in a custom renderer if wlr_renderer doesn't meet your needs. The
     * backend uses the renderer, for example, to fall back to software cursors
     * if the backend does not support hardware cursors (some older GPUs
     * don't). */
    if (!(server->backend = wlr_backend_autocreate(server->dpy, &server->session))) {
        wlr_log(WLR_ERROR, "couldn't create backend");
        exit(EXIT_FAILURE);
    }

    server->renderer = wlr_renderer_autocreate(server->backend);
    if (!server->renderer) {
        wlr_log(WLR_ERROR, "unable to create renderer");
        exit(EXIT_FAILURE);
    }
    wlr_renderer_init_wl_display(server->renderer, server->dpy);

    server->alloc = wlr_allocator_autocreate(
        server->backend, server->renderer);
    if (!server->alloc) {
        wlr_log(WLR_ERROR, "unable to create allocator");
        exit(EXIT_FAILURE);
    }

    // wl_list_init(&server->views);
    // wl_list_init(&server->unmanaged_surfaces);

    server->scene = wlr_scene_create();
    if (!server->scene) {
        wlr_log(WLR_ERROR, "unable to create scene");
        exit(EXIT_FAILURE);
    }

    output_init(server);
    seat_init(server);

    return server;
}

void server_free(Server *server)
{
    wlr_backend_destroy(server->backend);
    wl_display_destroy(server->dpy);
    free(server);
}

void server_run(Server *server, const char *startup_cmd)
{
    pid_t startup_pid = -1;

    /* Add a Unix socket to the Wayland display. */
    const char *socket = wl_display_add_socket_auto(server->dpy);
    if (!socket)
        die("startup: display_add_socket_auto");
    setenv("WAYLAND_DISPLAY", socket, 1);

    /* Now that the socket exists, run the startup command */
    if (startup_cmd) {
        int piperw[2];
        if (pipe(piperw) < 0)
            die("startup: pipe:");
        if ((startup_pid = fork()) < 0)
            die("startup: fork:");
        if (startup_pid == 0) {
            dup2(piperw[0], STDIN_FILENO);
            close(piperw[0]);
            close(piperw[1]);
            execl("/bin/sh", "/bin/sh", "-c", startup_cmd, NULL);
            die("startup: execl:");
        }
        dup2(piperw[1], STDOUT_FILENO);
        close(piperw[1]);
        close(piperw[0]);
    }
    /* If nobody is reading the status output, don't terminate */
    signal(SIGPIPE, SIG_IGN);

    /* Start the backend. This will enumerate outputs and inputs, become the DRM
     * master, etc */
    if (!wlr_backend_start(server->backend))
        die("startup: backend_start");

    // wl_display_run(server->dpy);
    g_main_loop_run(server->glib_loop);

    if (startup_cmd) {
        kill(startup_pid, SIGTERM);
        waitpid(startup_pid, NULL, 0);
    }
}

void server_quit(Server *server)
{
    wl_display_terminate(server->dpy);
}
